local L = AceLibrary("AceLocale-2.2"):new("GhostPulse")

local CHECK_INTERVAL = 5.0

local GetTime = _G.GetTime
local pairs = _G.pairs

local pots = {
	[L["Bottled Nethergon Energy"]] = {
		["Min"] = 1800,
		["Max"] = 3000,
		["AffectedByStone"] = true,
		["Zones"] = {
			[L["The Arcatraz"]] = true,
			[L["The Botanica"]] = true,
			[L["The Mechanar"]] = true,
			[L["Tempest Keep"]] = true,
		},
	},

	[L["Cenarion Mana Salve"]] = {
		["Min"] = 1800,
		["Max"] = 3000,
		["AffectedByStone"] = true,
		["Zones"] = {
			[L["The Slave Pens"]] = true,
			[L["The Steamvault"]] = true,
			[L["The Underbog"]] = true,
			[L["Serpentshrine Cavern"]] = true,
		},
	},

	[L["Mana Potion Injector"]] = {
		["Min"] = 1800,
		["Max"] = 3000,
		["AffectedByStone"] = true,
	},

	[L["Super Mana Potion"]] = {
		["Min"] = 1800,
		["Max"] = 3000,
		["AffectedByStone"] = true,
	},

	[L["Mana Emerald"]] = {
		["Name"] = L["Bottled Nethergon Energy"],
		["Min"] = 2340,
		["Max"] = 2460,
		["AffectedByStone"] = false,
	},

}


LifeTapWatcher = {

	New = function(this, env, listenerFunction)
		-- check arguments
		if not env or type(env) ~= "table" then
			error("Must supply an environment tracker to DecurseWatcher:New()")
		end
		
		if not listenerFunction or type(listenerFunction) ~= "function" then
			error("Must supply a listener function to LifeTapWatcher:New()")
		end
			
		-- globals
		local frame = CreateFrame("Frame")
		
		
		frame.watcherName = "LifeTap"
		
		frame.nextCheckTime = 0
		frame.lastShowedAlert = nil
		
		frame.enabled = false		
		frame.enabledAlerts = { }
		
		frame.availablePotions = { }
				
		frame.environment = env
		frame.OnDetect = listenerFunction
		
		-- functions
		frame:RegisterEvent("UNIT_MANA")
		frame:RegisterEvent("UNIT_HEALTH")
		frame:RegisterEvent("BAG_UPDATE")
		frame:RegisterEvent("BAG_UPDATE_COOLDOWN")
		
		frame.GetAlertTypes = function()
			return {
				["lifetap"] = L["Life Tap/Dark Pact"],
				["manapotion"] = L["Mana Potions/Gems"],
			}
		end
		
		-- common configuration
		frame.SetOptions = Environment.SetOptions		
		frame.CheckEnv = Environment.CheckEnv
		frame.CheckCombat = Environment.CheckCombat
		frame.CheckGrouping = Environment.CheckGrouping
		frame.CanShowThing = Environment.CanShowThing		
		frame.SetAlertEnabled = Environment.SetAlertEnabled
		
		-- watcher-specific function
		frame:SetScript("OnEvent", function(this, event, ...)
			if this.enabled then
				if event == "UNIT_MANA" or event == "UNIT_HEALTH" then
					if this.enabledAlerts["lifetap"] and Environment:GetPlayerClass() == "WARLOCK" then
						this:NotifyLifeTap(...)
					elseif this.enabledAlerts["manapotion"] then
						this:NotifyManaPotion()
					end
				elseif event == "BAG_UPDATE" or event == "BAG_UPDATE_COOLDOWN" then
					this:RefreshCurrentBagItems()
				end
			end
		end)
		
		frame.NotifyLifeTap = function(this, unit)
			local curTime = GetTime()
			
			
			if this.enabled and this.lastShowedAlert == nil or ( curTime >= this.lastShowedAlert + CHECK_INTERVAL ) then
				if unit == "player" then
					local showLT = false
					local showDP = false
					local hasDarkPact = (select(5, GetTalentInfo(1, 18)) == 1)
					local pointsImpLifetap = select(5, GetTalentInfo(1, 5))
					local ltAmount = this:GetLifetapValue(pointsImpLifetap)
					local ltHealthCost = this:GetLifetapHealthCost()
					local dpAmount = this:GetDarkPactValue()
					local maxMana = UnitManaMax("player")
					local curMana = UnitMana("player")
					local currentManaPercent = curMana / maxMana
					local diff = maxMana - curMana
					local showedAlert = false
					local potsEnabled = this.enabledAlerts["manapotion"] and this:CheckEnv("manapotion")
					local whichPotion = potsEnabled and this:WhichPotion()					
					local info = {
						["Unit"] = "player",
						["Type"] = "lifetap",
					}


					if not whichPotion then
						if hasDarkPact and diff >= dpAmount and this:PetMeetsReqs(dpAmount) then
							showDP = true
						end
						
						if showDP == false then
							if diff >= ltAmount and this:PlayerMeetsReqs(ltHealthCost) then
								showLT = true
							end
						end
	
						if showDP then
							info.Name = L["Dark Pact"]
							info.Spell = info.Name
							
							if this.OnDetect then
								if type(this.OnDetect) == "function" then
									this:OnDetect(this, "lifetap", L["Dark Pact"], "Interface\\Icons\\Spell_Shadow_DarkRitual", info)
								else
									error("LifeTapWatcher.OnDetect must be a function")
								end
							end
							
							showedAlert = true
						end
						
						if showLT then
							info.Name = L["Life Tap"]
							info.Spell = info.name
							
							if this.OnDetect then
								if type(this.OnDetect) == "function" then
									this:OnDetect(this, "lifetap", L["Life Tap"], "Interface\\Icons\\Spell_Shadow_BurningSpirit", info)
								else
									error("LifeTapWatcher.OnDetect must be a function")
								end
							end
	
							showedAlert = true
						end
										
						if showedAlert == false then
							this.lastShowedAlert = nil
						else
							this.lastShowedAlert = GetTime()
						end
					else
						this:NotifyManaPotion(whichPotion)
					end
				end
			end
		end
			
		frame.NotifyManaPotion = function(this, pot)
			local curTime = GetTime()
			
			
			if this.lastShowedAlert == nil or ( curTime >= this.lastShowedAlert + CHECK_INTERVAL ) then
				local whichPot = pot
				
				
				if whichPot == nil then
					whichPot = this:WhichPotion()				
				end
				
				if whichPot then
					local info = {
						["Unit"] = "player",
						["Name"] = whichPot.name,
						["Type"] = "manapotion",
					}
					
					if this.OnDetect then
						if type(this.OnDetect) == "function" then
							this:OnDetect(this, "manapotion", whichPot.name, whichPot.texture, info)
						else
							error("LifeTapWatcher.OnDetect must be a function")
						end
					end
	
					if showedAlert == false then
						this.lastShowedAlert = nil
					else
						this.lastShowedAlert = curTime
					end
				end
			end
		end
		
		frame.GetLifetapValue = function(this, pointsImpLT)
			local rc = 710
			local spi = UnitStat("player", 5)
			
			
			rc = rc + spi * 3.0
			rc = rc * ( 1 + ( 0.1 * pointsImpLT ) )
			
			return rc
		end
		
		frame.GetLifetapHealthCost = function(this)
			return this:GetLifetapValue()
		end
		
		frame.GetDarkPactValue = function(this)
			local rc = 700
			local plusShad = GetSpellBonusDamage(6)
			
			
			rc = rc + plusShad * 0.96
			
			return rc
		end
		
		frame.PlayerMeetsReqs = function(this, ltHealthCost)
			local rc = false
			
			
			if UnitHealth("player") >= ltHealthCost + 1 then
				rc = true
			end
			
			return rc
		end
		
		frame.PetMeetsReqs = function(this, dpAmount)
			local rc = false
			
			
			if UnitExists("playerpet") then
				if UnitCreatureFamily("playerpet") == L["Imp"] then
					local petMana = UnitMana("playerpet")
					
					
					if petMana >= dpAmount then
						rc = true
					end
				end
			end
				
			return rc
		end

		frame.WhichPotion = function(this)
			local rc = nil
			local hasStone = this:HasAlchemistStone()
			local manaLost = UnitManaMax("player") - UnitMana("player")
			
			for i, v in pairs(this.availablePotions) do
				local scalar = ( hasStone and 1.4 ) or 1
				local maxReturn = v.data.Max * scalar
				

				if manaLost >= maxReturn then
					if this:InZone(v.data.Zones) then
						-- check for cooldown
						local start, duration, enabled = GetContainerItemCooldown(v.bag, v.slot)


						if enabled then
							local canUseIt = false
							
							
							if start == nil then
								canUseIt = true
							elseif duration == nil then
								canUseIt = true
							elseif start + duration <= GetTime() then
								canUseIt = true
							end
							
							if canUseIt then
								-- we found one
								rc = v
								
								if table.isEmpty(v.data.Zones) == false then
									break
								end
							end
						end
					end
				end
			end
			
			return rc
		end
		
		frame.HasAlchemistStone = function()
			local rc = false
			local link
			
			
			for i = 13, 14 do
				link = GetInventoryItemLink("player", i)
				
				if link then
					link = string.lower(link)
					
					
					if string.find(link, "alchemist's stone") then
						rc = true
						break
					elseif string.find(link, "alchemist stone") then
						rc = true
						break
					end
				end
			end
		
			return rc
		end

		frame.InZone = function(this, zoneList)
			local rc = false
			
		
			if table.isEmpty(zoneList) then
				rc = true
			else
				local curZone = GetRealZoneText()
				

				for i, _ in pairs(zoneList) do
					if curZone == i then
						rc = true
						break
					end
				end
			end
			
			return rc
		end
		
		frame.RefreshCurrentBagItems = function(this)
			this.availablePotions = { }
										
			for bag = 0, 4 do
				for slot = 1, 40 do
					local link = GetContainerItemLink(bag, slot)
					

					if link then
						local name, _, _, _, _, _, _, _, loc = GetItemInfo(link)
						

						if this:IsPotion(name) then							
							local texture, count = GetContainerItemInfo(bag, slot)
							
							
							if texture then
								local info = { }
								
								
								info.name = name
								info.texture = texture
								info.bag = bag
								info.slot = slot
								info.data = pots[name]
								
								this.availablePotions[name] = info									
							end
						end
					end
				end
			end
		end

		frame.IsPotion = function(this, name)
			local rc = false
			
			
			if name then
				for i, _ in pairs(pots) do
					if i == name then
						rc = true
						break
					end
				end
			end
			
			return rc
		end
		
		return frame
	end,
	
}